struct的占用字节数
在 C 语言中,struct 的占用字节数不仅取决于各个成员类型的大小,还会受到 内存对齐(alignment) 的影响。下面我详细解释计算方法。

指针
指针的大小
一般指针的大小取决于系统总线地址宽度,32位系统的指针占4个字节,64位系统的指针占用的是8字节
指针的自增
指针自增自增多少个字节?取决于该变量的类型,该变量的类型占用几个字节,那么就自增几个字节
为什么声明要放在 .h文件
.h文件的作用就是让别人知道我有哪些接口/变量可以用- 如果声明写在
.h里,别的.c文件只要#include "Led.h"就能用,不需要每个.c文件自己去写一次extern - 这样保证了变量声明唯一来源,不容易写错名字或类型
宏定义
宏定义不能以分号结尾!!!
十六进制输出
%x → 小写字母,十六进制,不带 0x,不补零
%X → 大写字母,十六进制,不带 0x,不补零
%08X → 固定 8 位,前面补 0
手动写 "0x%08X" → 带上前缀大端小端是什么意思
1. 大端(Big Endian)和小端(Little Endian)是什么
它们指的是多字节数据(比如 int、short、float)在内存中的存放顺序。
- 大端模式(Big Endian):高字节存放在低地址,低字节存放在高地址。
- 内存从小到大排列:
高字节 → … → 低字节 - 类似我们写数字的方式:最高位在最前
- 内存从小到大排列:
- 小端模式(Little Endian):低字节存放在低地址,高字节存放在高地址。
- 内存从小到大排列:
低字节 → … → 高字节 - 就像把数字倒着存:最低位在最前
- 内存从小到大排列:
2. 举个例子
假设有个 32 位整数:
int a = 0x12345678;内存地址从低到高:
- 大端模式
地址: 0x00 0x01 0x02 0x03
数据: 0x12 0x34 0x56 0x78- 小端模式
地址: 0x00 0x01 0x02 0x03
数据: 0x78 0x56 0x34 0x123. 为什么会有大端和小端
- 大端:更符合人类阅读习惯(高位在前),很多网络协议规定使用大端,所以也叫 网络字节序。
- 小端:硬件实现简单,x86 系列 CPU 默认就是小端。
函数相关
ssanf
sscanf 是 C 标准库中一个非常实用的字符串解析函数,用于从字符串中按照指定格式提取数据,类似于 scanf 从标准输入读取,但 sscanf 是从**字符数组(字符串)**中读取。
函数原型
#include <stdio.h>
int sscanf(const char *str, const char *format, ...); //返回值为解析了几个项目测试程序
#include <stdio.h>
int main() {
char str[] = "123 45.67";
int a;
float b;
int ret = sscanf(str, "%d %f", &a, &b);
printf("解析了 %d 个项目\n", ret); // 输出:2
printf("a = %d, b = %.2f\n", a, b); // 输出:a = 123, b = 45.67
return 0;
}sprintf函数
sprintf 的第一个参数必须是 目标缓冲区(写入字符串的地方),第二个参数才是格式化字符串。
snprintf
限制拷贝多少个字符
// 限制版本号最多拷贝 31 个字符,确保结尾有 '\0'
snprintf(temp, sizeof(temp),
"{\"id\":\"1\",\"params\":{\"version\":\"%.*s\"}}",
26, OTA_Info.OTA_ver);sizeof
#include <stdio.h>
#include <string.h>
int main()
{
int buffer[64];
printf("sizeof(buffer) = %zu 字节\n", sizeof(buffer));
printf("sizeof(int) = %zu 字节\n", sizeof(int));
printf("元素个数 = %zu\n", sizeof(buffer) / sizeof(buffer[0]));
return 0;
}sizeof(buffer) = 256 字节
sizeof(int) = 4 字节
元素个数 = 64memset
函数原型
#include <string.h>
void *memset(void *s, int c, size_t n);假设我们定义了一个数组,然后直接输出,看看会怎么样
代码
#include <stdio.h>
#include <string.h>
int main()
{
int buffer[64];
for (int i = 0; i < sizeof(buffer)/sizeof(int); i++)
{
printf("%d ", buffer[i]);
}
return 0;
}下面是输出的结果
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2033488 0 256 0 2033496 0 8 0 2055568 0 -1571362666 32761 4205848 0 0 0 2033488 0 256 0 2033504 0 16 0 2055568 0 -1571362666 32761 8 0 0 0 2055600 0 4199920 0 0 0 0 0 8 0 4199705 0 8 0 23 0如果我们想让他们全部置为0,可以按下面这样操作
memset(buffer, 0, 64 * sizeof(int));注意:memset 是按字节设置内存,不适合设置非零 int 值(除非你明确知道字节布局)。
memcpy
memcpy 是 C 标准库中的一个非常重要的内存操作函数,用于从源内存地址复制指定字节数的数据到目标内存地址。它在 <string.h> 头文件中声明,是底层内存操作的核心函数之一,广泛用于数据拷贝、结构体复制、数组复制等场景。
和strcpy的区别是不会因为检测到\0就停止
测试程序
#include <stdio.h>
#include <string.h>
int main()
{
int src[5] = {1, 2, 3, 4, 5};
int dest[5];
memcpy(dest, src, sizeof(src)); // 复制整个数组 4*5个字节
for (int i = 0; i < 5; i++)
{
printf("%d ", dest[i]); // 输出:1 2 3 4 5
}
return 0;
}伪随机数生成器
增加简单 PRNG 函数:
// 简单的线性同余发生器
static uint32_t seed = 1234567;
static uint32_t SimpleRand(void) {
seed = seed * 1664525 + 1013904223;
return (seed >> 16) & 0x7FFF;
}修改获取值代码:
int temp = 15 + (SimpleRand() % 21); // 温度值: 15–35 ℃
int hum = 30 + (SimpleRand() % 61); // 湿度值: 30–90 %
int light = SimpleRand() % 1001; // 光照值: 0–1000 Lux
int conc = 10 + (SimpleRand() % 51); // 土壤湿度值: 10–60关键字
volatile 关键字
volatile
告诉编译器: 这个变量的值随时可能发生变化,不要对它做优化,每次都要从内存重新读取,而不是用寄存器缓存。
使用场景
中断与主循环共享的变量
- 例:中断里
flag=1;,主循环里检查if(flag)。
硬件寄存器
- STM32 的外设寄存器值可能会被硬件改,比如
USARTx->SR,要加volatile。
多任务环境(RTOS)
- 如果任务 A 改一个变量,任务 B 读取,也建议加。
extern 关键字
作用:声明变量或函数在别的文件定义,告诉编译器“这个东西是外部的”,不分配内存,只用来引用。
场景:跨文件访问全局变量时,在使用的
.c文件里用extern声明变量,真正定义在另一个.c文件里。在
.h文件中用extern声明一个在.c文件中定义的全局变量,是为了将该变量的接口暴露给外部模块使用,相当于“导出”这个变量的访问权限。